#ifndef TERR_REND_H
#define TERR_REND_H

#include "Atmosphere.h"

class CachedTexture;

typedef struct TILEDESC1 {	//[76 bytes]

	std::unique_ptr<CachedTexture> tex, tex_old, ntex, htex;
	ID3D11ShaderResourceView 
		/**tex,		//diffuse texture RGB
		*tex_old,*/	//from previous texture set
		*ltex,		//specular/emissive map RGBA
		/**ntex,		//normal map RGB
		*htex,*/		//heightmap
		*ctex;		//LCC texture

	UINT svtx;	//offset in tile buffer. [0 for not initialized]

	//collision and visibility detection
	float amin, amax, *HMAP;	//maximum and minimum altitude
	D3DXVECTOR3 bsPos;
	float bsRad;

	//links with subtiles
	struct TILEDESC1 *subtile[4];
	DWORD
		flags,	//misc flags
		ofs;	//offset in tile buffer
} TILEDESC1;

class CachedTexture
{
private:
	UINT64 m_offset;
	CComPtrEx<ID3D11ShaderResourceView> m_texture;
	UINT m_usageCount;
	TILEDESC1 * m_td;
	UINT m_flag;
	bool m_unloadable;
public:
	CachedTexture(TILEDESC1 * td, UINT flag, UINT64 offset);
	CachedTexture(ID3D11ShaderResourceView * texture);
	CachedTexture(ID3D11ShaderResourceView * texture, bool unloadable);
	virtual ~CachedTexture();

	void Unload();
	void Load(ID3D11ShaderResourceView * texture);
	UINT GetOffset();

	bool IsUnloadable();

	ID3D11ShaderResourceView ** GetTexture();

	bool IsLoaded();

	static void Init(std::unique_ptr<CachedTexture> & dest, ID3D11ShaderResourceView * tex);
};

//texture coord range
typedef struct TEXCRDRANGE1 {
	float tumin, tumax;
	float tvmin, tvmax;
} TEXCRDRANGE1;

struct TILECRDRANGE {
	TEXCRDRANGE1 range_tex;
	TEXCRDRANGE1 range_norm;
	TEXCRDRANGE1 range_height;
	TEXCRDRANGE1 range_lcover;
};

//Tile textures:
typedef struct TILETEX {	//textures
	CachedTexture * tex, *ntex, *htex;
	ID3D11ShaderResourceView
		//*tex,
		*ltex,
		/**ntex,
		*htex,*/
		*ctex;
} TILETEX;

typedef struct TILERENDERSPEC {
	TILEDESC1 *td;
	D3DXMATRIX mWorld;
	TILETEX Tex;
	TILECRDRANGE RANGE;
	UINT8 hemisphere, level;
	UINT ilat, nlat, ilng, nlng, flag;
	int ncascade;
	//166
} TILERENDERSPEC;


typedef struct TILEVERTEX_LOW {	//46
	D3DXVECTOR3 PosL;
	D3DXVECTOR3 NormL;
	D3DXVECTOR3 TangL;
	D3DXVECTOR2 TCrd;
} TILEVERTEX_LOW;

typedef struct TILEVERTEX_HIGH {
	D3DXVECTOR2 PosL;
	D3DXVECTOR2 TCrd;
	UINT32 flag;
} TILEVERTEX_HIGH;

typedef struct TILEMESH1 {
	ID3D11Buffer *VB, *IB;
	UINT
		nvtx,
		nidx,
		nface;
	D3DXVECTOR3 bsPos;
	float bsRad;
} TILEMESH1;

//============================================================================
//				Rendering of tile meshes
//============================================================================

#define WIREFRAME_COLOR_MULT 2.0f
#define NUM_PLANET_SHADOW 4U

#define PLANETARY_SHADOW_DIST_LIMIT 2e6

bool ReadTexture( BYTE **data, char *fname, UINT &twidth, UINT &theight );
bool GetMipMapNumber( UINT w, UINT h, UINT &nmip );

class TileRenderMgr {
	friend class DetailsManager;

#pragma region cbuffers

	//==============================================
	//			Misc features
	//==============================================

	//atnosphere haze, basic atmosphere model and 
	typedef struct cb_AtmParams {
		D3DXVECTOR3 FogColor;
		float FogDensity;

		D3DXVECTOR3 SunDir;
		float Dispersion;

		float SunAppRad;
		float Ambient0;
		float GlobalAmb;
		float dist_scale;

		D3DXVECTOR3 PlanetPosW;
		float exposure;
		//64
	} cb_AtmParams;

	//eclipses:
	typedef struct cb_PlanetaryShadowParams {
		struct {
			D3DXVECTOR3 PosW;
			float rad;

			UINT hasAtm;
			float Dispersion;
			float nothing[2];
		} Planet[4];
		D3DXVECTOR3 SunPosW;
		UINT nplanet;
		//144
	} cb_PlanetaryShadowParams;

	//ring shadows params:
	typedef struct cb_RingParams {
		D3DXVECTOR3 NormRW;
		float irad;

		D3DXVECTOR3 PosPW;
		float orad;
		//32
	} cb_RingParams;

	//indexes and mults of diffuse and normal microtextures:
	typedef struct MicroTexSpec {
		UINT tidx;
		float tmult;
		UINT nidx;
		float nmult;
		//16
	} MicroTexSpec;

	//============================
	//			Terrain Shadows
	//============================
	typedef struct VSCB_Shadow_per_tile {
		D3DXMATRIX W;
		float TexOff_height[4];

		float coef_lat_0;
		float coef_lat_1;
		float coef_lng;
		UINT flags;

		float cos_minlat;
		float sin_minlat;
		float dist_lat;
		float dist_lng;
		//112
	} VSCB_Shadow_per_tile;

	//============================
	//			Tiles
	//============================
	typedef struct VSCB_per_frame {
		D3DXMATRIX VP;
		float dist_scale;
		float coef_altitude;
		float waste;
		float disp_microtex_range;
		//64
	} VSCB_per_frame;

	//
	typedef struct VSCB_per_tile {
		D3DXMATRIX W;
		float TexOff[4];
		float TexOff_norm[4];
		float TexOff_height[4];
		float TexOff_lcover[4];

		float coef_lat_0;
		float coef_lat_1;
		float coef_lng;
		UINT flags;

		float cos_minlat;
		float sin_minlat;
		float dist_lat;
		float dist_lng;
		//160
	} VSCB_per_tile;	//atmosphere parameters for all FL and DetailsManager

	typedef struct HSCB_per_tile {
		float edge_tf;
		float min_inside_tf;
		float max_inside_tf;
		float coef_diff;
		//16
	} HSCB_per_tile;

	typedef struct PSCB_per_frame {
		D3DXVECTOR3 SunDir;
		float water_spower;
		D3DXVECTOR3 Ambient;
		float coef_altitude;
		//32
	} PSCB_per_frame;

	typedef struct PSCB_per_tile {
		UINT32 flags;
		float max_microtex_dist;	//maximum distance where microtex is still used
		float color_mult;
		float waste;
		//16
	} PSCB_per_tile;

	typedef struct TILE_RES_LVL {
		UINT vdim;
		float edge_tf[2];
		float inside_tf[2];

		UINT sidx[2];
		UINT nidx[2];
		UINT svtx[2];
		UINT nvtx[2];

	} TILE_RES_LVL;

#pragma endregion

public:
	TileRenderMgr( TerrainManager *_term, PlanetaryConfig *_CFG, TILEMESH1 ***_TPL );
	~TileRenderMgr();

	static void GlobalInit( ID3D11Buffer **_CB_AtmParams, ID3D11Buffer **_CB_PlanetShadows );
	static void GlobalExit();

	void UpdatePerFrameBuffers( float dist_scale, float coef_altitude, float luminance );
	void RenderSphere( UINT8 level, UINT npatch, TILEDESC1 *td, D3DXMATRIX *mWorld, float dist_scale, float luminance );	
	void RenderLowResTiles( TILERENDERSPEC *trs, UINT ntile, float dist_scale, float luminance );
	void RenderHighResTiles( TILERENDERSPEC *trs, UINT ntile, float dist_scale, float coef_altitude, float luminance );

	void RenderTerrainShadows( std::vector<TILERENDERSPEC> &trs, float dist_scale, float coef_altitude );
	
	static void SetWaterMicroTexture( const char *fname );

	float InitResources( float **flat_disp_data, float **coarse_disp_data, D3D11Client * client );
private:
	bool AddTexture( std::vector<BYTE*> &TEX, BYTE *data, UINT *twidth, UINT *theight, UINT &nmip );

	static void CreateSphere( TILEMESH1 &mesh, UINT nrings, bool hemisphere, int which_half, int texres );
	static void CreateSpherePatch_LOW( TILEMESH1 &mesh, UINT ilat, UINT nlat, UINT nlng, UINT res, UINT bseg = 0xFFFFFFFF, bool reduce = true, bool shift_origin = false );
	static void CreateSpherePatch_HIGH( TILEVERTEX_HIGH *vtx, UINT16 *Idx, UINT ilat, UINT res, UINT bseg = 0xFFFFFFFF );

	PlanetaryConfig *CFG;
	TerrainManager *term;
	AtmosphereMgr *amgr;
	RingManager	*rmgr;

	std::vector<D3D10_SHADER_MACRO> SM;	

	bool
		bShowWireFrame;
	
	//same for any planet
	static UINT FL;		//current feature level of Dev

	static CComPtrEx<ID3D11InputLayout> IL_TILEVERTEX_HIGH;
	static ID3D11InputLayout
		*IL_TILEVERTEX_LOW;///,		//xyz positions		L1-L7
		//*IL_TILEVERTEX_HIGH;	//angular positions	L8-LMAX
	static UINT
		Stride_TILEVERTEX_LOW,		//L1-L7		with xyz coords
		Stride_TILEVERTEX_HIGH;		//L8-LMAX	with angular coords
	static ID3D11VertexShader
		*VS_Tile_Far[2];//basic atmosphere + other
	static ID3D11SamplerState
		*SS_HeightMap,
		*SS_TileTexture,
		*SS_MicroTexture;
	static ID3D11ShaderResourceView
		*WaterMicroTex;
	static ID3D11Buffer
	//tile mesh:
		*VB_TPL, *IB,		//vertex/index buffers for levels L8-L18
	//terrain:
		*CB_AtmParams,			//atmosphere and constant lighting parameters
		*CB_PlanetShadowParams,	//data required for computation of planetary shadows
		*CB_RingParams,
		*VSCB_Tile[2],
		*HSCB_Tile,
		*PSCB_Tile[2];
	static ID3D11DepthStencilState
		*DSS_Tile;
	static UINT patchidx[9];	//indexes of starting TILEDESC1 structs of specific levels in tiledesc
	static UINT NLAT[9];		//number of latitude segments for L1-L8
	static UINT NLNG5[1], NLNG6[2], NLNG7[4], NLNG8[8], *NLNG[9];
	static TILEMESH1 TPL_1,	TPL_2, TPL_3, TPL_4[2], TPL_5, TPL_6[2], TPL_7[4],
		*TPL[8];			//low-res tile meshes
	static TILE_RES_LVL TRES[3];//12/12 [I], 34/18 [II], 66/26 [III]

	//compiled for each planet (actual code depends on config settings)
	CComPtrEx<ID3D11VertexShader>
		VS_Tile;
	CComPtrEx<ID3D11HullShader>
		HS_Tile;
	CComPtrEx<ID3D11DomainShader>
		DS_Tile;
	CComPtrEx<ID3D11PixelShader>
		PS_Tile;
	ID3D11ShaderResourceView
		*DisplacementMicroTex,
		*aTerrainMicroTex;
	ID3D11Buffer
		*CB_ForestMicro;

	//shadows:
	struct CBShadow_per_frame
	{
		float dist_scale;
		float coef_altitude;
		float nothing[2];
	};

	struct CBShadow_per_tile
	{
		D3DXMATRIX mWorld;
		float TexOff_height[4];
		float coef_lat_0;
		float coef_lat_1;
		float coef_lng;
		UINT ncascade;

		float cos_minlat;
		float sin_minlat;
		float nothing[2];
	};

	static CComPtrEx<ID3D11VertexShader>
		VS_Shadow;
	static CComPtrEx<ID3D11GeometryShader>
		GS_Shadow;
	static CComPtrEx<ID3D11PixelShader>
		PS_Shadow;
	static CComPtrEx<ID3D11Buffer>
		cb_per_frame,
		cb_per_tile;

	static void InitTerrainShadows();
	static void ExitTerrainShadows();

public:
	const D3D10_SHADER_MACRO* GetShaderConfig() { return SM.size() > 0 ? &SM[0] : nullptr; }
};

//============================================================================
//				Rendering of details on terrain (trees, rocks, etc.)
//============================================================================

#pragma region details rendering

#define NPOINT 512U
#define DETAILS_TILELVL 16U
#define CITY_CTYPE 1024U	//flag

class DetailsManager {

	//vegetation/rock vertex
	typedef struct POINTVERTEX {
		D3DXVECTOR3 PosL;
		D3DXVECTOR3 NormL;
		D3DXVECTOR3 TangL;
		D3DXVECTOR2 TCrd;
		float dist;
		float rnd;
		//
	} POINTVERTEX;

	//bulding vertex (created from mesh triangles)
	typedef struct TRIANGLEVERTEX {
		D3DXVECTOR3 cntPosL;
		D3DXVECTOR3 wPosL;
		D3DXVECTOR3 hPosL;
		D3DXVECTOR2 TCrd;	//tile texture coord
		UINT tidx;			//type index
	} TRIANGLEVERTEX;

	//one structure per city mesh
	struct UMESH {
		UINT svtx, nvtx;
		ID3D11Buffer *VB;//vertex buffer with TRIANGLEVERTEX vertexes
		ID3D11ShaderResourceView *aTextures;//mesh diffuse, normal and emissive textures
	};

/*	typedef struct VERTEX_URBAN {
		D3DXVECTOR3 PosL;
		float spower;
		UINT tidx;
	} VERTEX_URBAN;*/

	typedef struct VSCB_per_tile {
		D3DXMATRIX W;

		float TexOff[4];
		float TexOff_height[4];
		float TexOff_lcover[4];

		UINT flags;
		float waste[3];
		//96
	} VSCB_per_tile;
	
public:
	DetailsManager( TerrainManager *_term, PlanetaryConfig *_CFG );
	~DetailsManager();

	static void GlobalInit( ID3D11Buffer *_CB_AtmParams, ID3D11Buffer *_CB_EclipseParams );
	static void GlobalExit();

	void InitResources();

	void Render( TILERENDERSPEC *TRS, UINT ntile );

private:
	bool AddTexture( std::vector<BYTE*> &TEX, BYTE *data, UINT *twidth, UINT *theight, UINT &nmip );
	bool AddTextureUrban( std::vector<BYTE*> &TEX, BYTE *data, UINT *twidth, UINT *theight, UINT &nmip );

	void ProcessTile( UINT8 level, UINT8 hemisphere, UINT ilat, UINT nlat, UINT ilng, UINT nlng, TILEDESC1 *td, TILECRDRANGE RANGE, TILETEX Tex, TILECRDRANGE bkp_RANGE, TILETEX bkp_Tex );
	void ExpandTRSBuffer( TILERENDERSPEC *TRS, UINT &size );
	void TileWorldMatrix( UINT8 hemisphere, UINT ilat, UINT nlat, UINT ilng, UINT nlng, D3DXMATRIX &mWorld );

	TerrainManager *term;
	PlanetaryConfig *CFG;
	static struct RENDERPARAM {
		D3DXMATRIX mWorld;
		D3DXMATRIX mWorld_tmp;
		MATRIX3 grot;
		VECTOR3 cpos;
		double objsize;
	} RP;
	static TILERENDERSPEC
		*TRS_AVG,
		*TRS_HIGH,
		*TRS_CITY;
	static UINT
		ntile_avg, size_avg,
		ntile_high, size_high,
		ntile_city, size_city;
	float Details_maxdist;//max(Urban,max(Average,High))

	//=============================================
	//			Average/High vegetation/rocks
	//=============================================
	static UINT					Stride_POINTVERTEX;
	static ID3D11InputLayout	*IL_POINTVERTEX;
	static ID3D11Buffer
		*VB_AverageHigh,		//random point vertex buffer
		*VSCB_Tile,
		*CB_AtmParams,
		*CB_PlanetShadowParams,
		*CB_CoverTypeParams;
	static ID3D11VertexShader	*VS_AverageHigh;
	ID3D11GeometryShader		*GS_Average, *GS_High;
	ID3D11PixelShader			*PS_Average, *PS_High;
	ID3D11ShaderResourceView	*aTextures_AverageHigh;//texture array
	static ID3D11SamplerState
		*SS_HeightMap,			//<= TileRenderMgr
		*SS_TileTexture,		//<= TileRenderMgr
		*SS_TextureAverageHigh,	//linear
		*SS_TextureCity;		//anisotropic
	static ID3D11DepthStencilState
		*DSS_Tile;			//<= TileRenderMgr

	//=============================================
	//			City
	//=============================================
	std::vector<UMESH> aMESH;
	static UINT					Stride_TRIANGLEVERTEX;
	static ID3D11InputLayout	*IL_TRIANGLEVERTEX;
	static ID3D11VertexShader	*VS_City;
	ID3D11GeometryShader		*GS_City;
	ID3D11PixelShader			*PS_City;
};

#pragma endregion

/*	//============================
	//		High
	//============================

	typedef struct MESHPARAM {
		UINT nidx;
		UINT sidx;
		UINT svtx;
		UINT nvtx;

		float BSRad;
		float nothing[3];
		//32
	} MESHPARAM;

	typedef struct CoverTypeParams {
		float width;
		float height;
		float ref_height;
		UINT flags;

		float max_dist;
		UINT tidx;
		UINT nidx;
		float notning;
		//32
	};

	typedef struct CSCB_High_per_frame {
		D3DXMATRIX V;
		D3DXMATRIX W[4];

		UINT ntile;
		float dfr;
		float h;
		float hf;

		float w;
		float wf;
		float nothing[2];
		//352
	} CSCB_High_per_frame;
	
	/*	static UINT
		Stride_POINTVERTEX,
		Stride_ArgBuffer,
		Stride_MESHVERTEX,
		Stride_INSTANCEDATA;
	static ID3D11InputLayout
		*IL_POINTVERTEX,
		*IL_MESHVERTEX;
	static ID3D11VertexShader
		*VS_Average,
		*VS_High;
	static ID3D11Buffer
		*CB_AtmParams,
		*CB_PlanetShadowParams,
		*CB_LightingHigh,
		*VB_Average,
		*ArgumentBuffer,
		*VB_InstanceData,
		*VSCB_Average,
		*CSCB_High;
	static ID3D11Resource
		*aHeightMaps4,
		*aLCoverMaps4,
		*aTextures4;
	static ID3D11ShaderResourceView
		*VB_AverageSRV,
		*LCoverDistortionSRV,

		*aHeightMaps4SRV,
		*aLCoverMaps4SRV,
		*aTextures4SRV;
	static ID3D11DepthStencilState
		*DSS_Details;
	static ID3D11SamplerState
		*SS_TileTexture,
		*SS_BillBoardTexture,
		*SS_MeshTexture;
	static ID3D11UnorderedAccessView
		*ArgumentBufferUAV,
		*InstanceDataUAV;

	//planet-specific:
	PlanetaryConfig *CFG;
	TerrainManager *term;

	ID3D11GeometryShader
		*GS_Average;
	ID3D11ComputeShader
		*CS_High;
	ID3D11PixelShader
		*PS_Average,
		*PS_High;
	ID3D11Buffer
		*VB_MeshHigh, *IB_MeshHigh,
		*CB_CoverTypeParams,
		*CB_MeshParams;
	ID3D11ShaderResourceView
		*MeshParamsSRV,
		*aTexturesAverage,
		*aTexturesHigh;*/

#endif